﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Management.Automation.Runspaces;
using System.Text;
using System.Xml.Linq;
using Microsoft.Data.Caching.AdminApi;
using System.ComponentModel;

namespace MDCAdminTool
{
    public class CacheCluster : PowerShellBase<object , CacheHost>
    {
        #region Fields
        private string        _Provider;
        private string        _ConnectionString;
        private bool          _IsOpen;
        private string        _ConfigFile;
        private string        _ImportConfigFile;
        private string        _ExportConfigFile;
        private Collection<CacheHost> _Hosts;
        #endregion

        #region Properties
        public string Provider
        {
            get { return _Provider; }
            set { _Provider = value; OnPropertyChanged( "Provider" ); }
        }
        public string ConnectionString
        {
            get { return _ConnectionString; }
            set { _ConnectionString = value; OnPropertyChanged( "ConnectionString" ); }
        }
        public bool IsOpen
        {
            get { return _IsOpen; }
            set { _IsOpen = value; OnPropertyChanged( "IsOpen" ); }
        }
        public string ConfigFile
        {
            get { return _ConfigFile; }
            set { _ConfigFile = value; OnPropertyChanged( "ConfigFile" ); }
        }
        public string ExportConfigFile
        {
            get { return _ExportConfigFile; }
            set { _ExportConfigFile = value; OnPropertyChanged( "ExportConfigFile" ); }
        }
        public string ImportConfigFile
        {
            get { return _ImportConfigFile; }
            set { _ImportConfigFile = value; OnPropertyChanged( "ImportConfigFile" ); }
        }
        
        public Collection<CacheHost> Hosts
        {
            get { return _Hosts; }
            set { _Hosts = value; OnPropertyChanged( "Hosts" ); }
        }

        

        #endregion

        private DataCacheConfigSection dataCacheConfig;

        public CacheCluster()
        {
            Type  = CacheType.CacheCluster;
            Name  = "No Cluster";
            Hosts = new Collection<CacheHost>();
        }
        public CacheCluster( string configFile ) : this()
        {
            InitCacheCluster( configFile );
        }

        #region Cache Cluster-related Commands

        /// <summary>
        /// Sets the context of your PowerShell session to the desired cache cluster.
        /// When starting a PowerShell session on a cache host, this command runs automatically, 
        /// by using the connection parameters specified in the cache host configuration file.
        /// </summary>
        /// <param name="name">The name of the cache cluster.</param>
        /// <param name="provider">The provider that is used to store the cluster configuration settings.</param>
        /// <param name="connectionString">The connection string to the database.</param>
        public void UseCacheCluster( string name , string provider , string connectionString )
        {
            Name             = name;
            Provider         = provider;
            ConnectionString = connectionString;

            UseCacheCluster();
        }
        /// <summary>
        /// Sets the context of your PowerShell session to the desired cache cluster.
        /// When starting a PowerShell session on a cache host, this command runs automatically, 
        /// by using the connection parameters specified in the cache host configuration file.
        /// </summary>
        public void UseCacheCluster()
        {
            var p = new Dictionary<string , object>
                        {
                            {"ConnectionString", ConnectionString},
                            {"Name"            , Name},
                            {"Provider"        , Provider}
                        };
            //if ( CheackConnectionStringAndProvider() )
            {
                Invoke( "Use-CacheCluster" , p );
            }
            //else
            //{
            //    Log( new ErrorMessage { Message = "Bad connection string or provider !" } );
            //}
        }

        /// <summary>
        /// Starts all cache host services in the cluster. Lead hosts are started first.
        /// </summary>
        public override void Start()
        {
            Invoke( "Start-CacheCluster" );
            LoadCacheClusterInfo();
            LoadCacheHosts();
        }

        /// <summary>
        /// Stops all cache host services in the cluster.
        /// </summary>
        public override void Stop()
        {
            Invoke( "Stop-CacheCluster" );
            LoadCacheHosts();
        }

        /// <summary>
        /// Restarts all cache host services in the cluster in the correct sequence.
        /// </summary>
        public override void Restart()
        {
            Invoke( "Restart-CacheCluster" );
            UseCacheCluster();
            LoadCacheClusterInfo();
            LoadCacheHosts();
        }

        /// <summary>
        /// This command exports the cluster configuration settings, as they currently exist in the cluster, 
        /// to the specified XML-based configuration file. This command can be used regardless of 
        /// where you chose to store your cluster configuration settings (SQL Server or shared network folder).
        /// </summary>
        /// <param name="file">The fully qualified path and name for the XML-based configuration file. If a file already exists by this name, it will be overwritten.</param>
        public void ExportConfig()
        {
            var p = new Dictionary<string , object> { { "File" , ExportConfigFile } };
            Invoke( "Export-CacheClusterConfig" , p );
        }

        /// <summary>
        /// This command imports the cluster configuration settings as described in the specified 
        /// XML-based configuration file. This command can be used regardless of where you chose to 
        /// store your cluster configuration settings (SQL Server or shared network folder).
        ///  You must restart the cluster with the Restart-CacheCluster for any changes to take effect.
        /// </summary>
        /// <param name="file">The fully qualified path and name of the XML-based configuration file that describes the cache cluster configuration settings to be applied to the cluster. </param>
        public void ImportConfig()
        {
            var p = new Dictionary<string , object> { { "File" , ImportConfigFile } };
            Invoke( "Import-CacheClusterConfig" , p );
        }

        /// <summary>
        /// Load all cache host services that are members of the cache cluster.
        /// </summary>
        public void LoadCacheHosts()
        {
            var r = Invoke( "Get-CacheHost" );
            if ( r == null ) return;

            var hosts = new Collection<CacheHost>();

            foreach ( var host in r )
            {
                if ( host.BaseObject is HostInfo )
                {
                    var h = host.BaseObject as HostInfo;
                    hosts.Add( new CacheHost( h , this ) );
                }
            }
            Hosts = hosts;
        }
        public override void LoadChildren()
        {
            LoadCacheHosts();
        }


        #endregion

        #region Helper Method
        private bool CheackConnectionStringAndProvider()
        {
            bool result = false;
            DbConnection conn = null;
            try
            {
                var factory = DbProviderFactories.GetFactory( Provider );
                conn = factory.CreateConnection();
                conn.ConnectionString = ConnectionString;
                conn.Open();
                result = true;
            }
            catch ( Exception ex )
            {
                Log( new ErrorMessage { Message =  ex.Message } );
                result = false;
            }
            finally
            {
                if( conn != null )
                    conn.Dispose();
            }


            return result;

        }
        public void LoadCacheClusterInfo()
        {
            try
            {
                if ( File.Exists( ConfigFile ) )
                {
                    InitDataCacheConfigSection( ConfigFile );
                }
                else
                {
                    Log( new ErrorMessage { Message =  "Can't find the DistributedCache123.exe.config file." } );
                }
            }
            catch ( Exception ex )
            {
                Log( new ErrorMessage { Message =  ex.Message } );
            }
        }
        private void LoadCacheClusterInfo( string configFile )
        {
            ConfigFile = configFile;
            LoadCacheClusterInfo();
        }
        private void InitDataCacheConfigSection( string file )
        {
            var config  = XElement.Load( file );
            XElement dcc = config.Descendants( "dataCacheConfig" ).First();
            if ( dcc == null )
            {
                Log( new ErrorMessage { Message =  "Can't find the <dataCacheConfig> tag" } );
                return;
            }
            dataCacheConfig = new DataCacheConfigSection
            {
                ClusterName   = dcc.Attribute( "clusterName" ).Value ,
                HostName      = dcc.Attribute( "hostName" ).Value ,
                CacheHostName = dcc.Attribute( "cacheHostName" ).Value ,
                Provider      = dcc.Element( "clusterConfig" ).Attribute( "provider" ).Value ,
                ConnectionString = dcc.Element( "clusterConfig" ).Attribute( "connectionString" ).Value
            };

            InitCacheCluster( dataCacheConfig );
        }
        public void InitCacheCluster( DataCacheConfigSection dccs )
        {
            Name             = dccs.ClusterName;
            Provider         = dccs.Provider;
            ConnectionString = dccs.ConnectionString;
        }
        public void InitCacheCluster( string configFile )
        {
            ConfigFile = configFile;
            try
            {
                // Step 1: Get Config Info.
                LoadCacheClusterInfo();

                // Step 2: Chooce Cache Cluster
                UseCacheCluster( Name , Provider , ConnectionString );

                // Must be last.
                LoadCacheHosts();
            }
            catch ( Exception ex )
            {
                Log( new ErrorMessage { Message =  ex.Message } );
            }
        }
        #endregion

        #region Not Implemented
        public override CacheStats LoadStatistics()
        {
            throw new NotImplementedException();
        }
        public override void LoadConfig()
        {
            throw new NotImplementedException();
        }
        public override void SaveConfig()
        {
            throw new NotImplementedException();
        }
        public override void Add( CacheHost name )
        {
            throw new NotImplementedException();
        }
        public override void Remove( CacheHost name )
        {
            throw new NotImplementedException();
        }
        public override void Add( CacheEntityBase item )
        {
            throw new NotImplementedException();
        }
        public override void Remove( CacheEntityBase item )
        {
            throw new NotImplementedException();
        }
        #endregion

        
    }
}
